﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.IO;
using System.Net;
using Lidgren.Network;

namespace SyncData_Server
{
    public enum Headers // Packet Headers. Optional but useful later on.
    {
        GameWorld, // Request/Receive Information about GameWorld
        PlayerName // Send/Receive Information about Player Name alteration
    }

    class Program
    {
        static NetServer Server; // This will store our Server information.
        static NetPeerConfiguration Config; // This will store out Server configuartion.
        static List<Player> Players; // This will hold all the Players information on the server.

        static void Main(string[] args)
        {
            SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
            //Set dummy sync context
            Console.WriteLine("This is the server"); // This will be our server.
            Players = new List<Player>(); // Initialize player's list
            Config = new NetPeerConfiguration("MyGame"); // AppID use to recognize 
            Console.WriteLine("Server configuration created.");
            // server and client's peer. Make sure both are same on Client and Server.
            Config.Port = 7777; // Our port for the server.
            Config.EnableMessageType(NetIncomingMessageType.ConnectionApproval);
            //Enable connection approval message type. In Lidgren all the messages
            //are split into message types and you can ignore some or all of'em
            Server = new NetServer(Config); // Initialize with the Config.
            Console.WriteLine("Server socket initialized.");
            Server.RegisterReceivedCallback(new SendOrPostCallback(ReceiveData));
            // This will call the "ReceiveData" callback whenever the server receives data
            // from a client in a seperate thread.
            Server.Start(); // Start our server.
            Console.WriteLine("Server started.");

            new Thread(new ThreadStart(delegate // An example Input Manager to get respond 
                // on Console
                {
                    string input;

                    while ((input = Console.ReadLine()) != null)
                    {
                        Console.WriteLine("New Input.");
                        string[] prms = input.Split(' '); // Params for the input.

                        switch (prms[0]) // The first param is the command name.
                        {
                            // No commands on server as of now
                        }
                    }

                })).Start();
        }

        private static void ReceiveData(object state)
        {
            NetIncomingMessage incmsg; // Incoming message. Which is received from client.
            NetOutgoingMessage outmsg; // Outgoing message. Which is sending to client.

            while ((incmsg = Server.ReadMessage()) != null) // If there's any new message
            {
                Console.WriteLine("Received Data.");

                switch (incmsg.MessageType) // See the type of the message.
                {
                    case NetIncomingMessageType.ConnectionApproval:
                        // If the client is requesting for it's connection to be approved

                        Console.WriteLine("Client " + incmsg.SenderConnection.ToString() + " connected and approving.");
                        incmsg.SenderConnection.Approve(); // Approve it
                        Console.WriteLine("Connection approved");
                        break;

                    case NetIncomingMessageType.Data:

                        Console.WriteLine("Received data from client.");
                        Headers header = (Headers)incmsg.ReadByte();
                        // Get the header type which is the byte send from client.

                        switch (header)
                        {
                            case Headers.GameWorld:

                                Console.WriteLine("Requested game world update.");
                                Player newPlayer = new Player();
                                newPlayer.Name = "Player " + Players.Count + 1;
                                newPlayer.Age = 15;
                                newPlayer.Health = 100;
                                newPlayer.IP = incmsg.SenderEndPoint.Address.ToString();
                                newPlayer.Connection = incmsg.SenderConnection;
                                Players.Add(newPlayer);
                                outmsg = Server.CreateMessage(); // Create a message
                                outmsg.Write((byte)Headers.GameWorld); // It will be a GameWorld Update
                                outmsg.Write(Players.Count); // Send how many players the server has.
                                foreach (Player player in Players) outmsg.WriteAllProperties(player);
                                // Write all the information of each individual player object
                                Server.SendToAll(outmsg, NetDeliveryMethod.UnreliableSequenced);
                                // Send the message to all clients on the server.
                                Console.WriteLine("Send game world update.");
                                break;

                            case Headers.PlayerName:

                                break;
                        }

                        Server.Recycle(incmsg); // Object Polling for Optimization.
                        Console.WriteLine("Recycled data.");
                        break;
                }
            }
        }
    }

    public class Player
        // A basic player class. Make sure whatever you want to send back/forth is
        // a property and not a field. Like I don't want to send Connection back/forth
        // to the server so I make it on a field. 
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public int Health { get; set; }
        public string IP { get; set; } // Hold the IP of the player
        public NetConnection Connection; // Hold the Client Connection of the player

        public Player(){ }
    }
}
